1   /**
2    * Licensed to the Apache Software Foundation (ASF) under one
3    * or more contributor license agreements.  See the NOTICE file
4    * distributed with this work for additional information
5    * regarding copyright ownership.  The ASF licenses this file
6    * to you under the Apache License, Version 2.0 (the
7    * "License"); you may not use this file except in compliance
8    * with the License.  You may obtain a copy of the License at
9    *
10   * http://www.apache.org/licenses/LICENSE-2.0
11   *
12   * Unless required by applicable law or agreed to in writing, software
13   * distributed under the License is distributed on an "AS IS" BASIS,
14   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15   * See the License for the specific language governing permissions and
16   * limitations under the License.
17   */
18  package storm.starter.tools;
19  
20  import backtype.storm.utils.Time;
21  import org.testng.annotations.DataProvider;
22  import org.testng.annotations.Test;
23  
24  import static org.fest.assertions.api.Assertions.assertThat;
25  
26  public class NthLastModifiedTimeTrackerTest {
27  
28    private static final int ANY_NUM_TIMES_TO_TRACK = 3;
29    private static final int MILLIS_IN_SEC = 1000;
30  
31    @DataProvider
32    public Object[][] illegalNumTimesData() {
33      return new Object[][]{ { -10 }, { -3 }, { -2 }, { -1 }, { 0 } };
34    }
35  
36    @Test(expectedExceptions = IllegalArgumentException.class, dataProvider = "illegalNumTimesData")
37    public void negativeOrZeroNumTimesToTrackShouldThrowIAE(int numTimesToTrack) {
38      new NthLastModifiedTimeTracker(numTimesToTrack);
39    }
40  
41    @DataProvider
42    public Object[][] legalNumTimesData() {
43      return new Object[][]{ { 1 }, { 2 }, { 3 }, { 20 } };
44    }
45  
46    @Test(dataProvider = "legalNumTimesData")
47    public void positiveNumTimesToTrackShouldBeOk(int numTimesToTrack) {
48      new NthLastModifiedTimeTracker(numTimesToTrack);
49    }
50  
51    @DataProvider
52    public Object[][] whenNotYetMarkedAsModifiedData() {
53      return new Object[][]{ { 0 }, { 1 }, { 2 }, { 3 }, { 4 }, { 5 }, { 8 }, { 10 } };
54    }
55  
56    @Test(dataProvider = "whenNotYetMarkedAsModifiedData")
57    public void shouldReturnCorrectModifiedTimeEvenWhenNotYetMarkedAsModified(int secondsToAdvance) {
58      // given
59      Time.startSimulating();
60      NthLastModifiedTimeTracker tracker = new NthLastModifiedTimeTracker(ANY_NUM_TIMES_TO_TRACK);
61  
62      // when
63      advanceSimulatedTimeBy(secondsToAdvance);
64      int seconds = tracker.secondsSinceOldestModification();
65  
66      // then
67      assertThat(seconds).isEqualTo(secondsToAdvance);
68  
69      // cleanup
70      Time.stopSimulating();
71    }
72  
73    @DataProvider
74    public Object[][] simulatedTrackerIterations() {
75      return new Object[][]{ { 1, new int[]{ 0, 1 }, new int[]{ 0, 0 } }, { 1, new int[]{ 0, 2 }, new int[]{ 0, 0 } },
76          { 2, new int[]{ 2, 2 }, new int[]{ 2, 2 } }, { 2, new int[]{ 0, 4 }, new int[]{ 0, 4 } },
77          { 1, new int[]{ 1, 1, 1, 1, 1, 1, 1 }, new int[]{ 0, 0, 0, 0, 0, 0, 0 } },
78          { 1, new int[]{ 1, 2, 3, 4, 5, 6, 7 }, new int[]{ 0, 0, 0, 0, 0, 0, 0 } },
79          { 2, new int[]{ 1, 1, 1, 1, 1, 1, 1 }, new int[]{ 1, 1, 1, 1, 1, 1, 1 } },
80          { 2, new int[]{ 2, 2, 2, 2, 2, 2, 2 }, new int[]{ 2, 2, 2, 2, 2, 2, 2 } },
81          { 2, new int[]{ 1, 2, 3, 4, 5, 6, 7 }, new int[]{ 1, 2, 3, 4, 5, 6, 7 } },
82          { 3, new int[]{ 1, 1, 1, 1, 1, 1, 1 }, new int[]{ 1, 2, 2, 2, 2, 2, 2 } },
83          { 3, new int[]{ 1, 2, 3, 4, 5, 6, 7 }, new int[]{ 1, 3, 5, 7, 9, 11, 13 } },
84          { 3, new int[]{ 2, 2, 2, 2, 2, 2, 2 }, new int[]{ 2, 4, 4, 4, 4, 4, 4 } },
85          { 4, new int[]{ 1, 1, 1, 1, 1, 1, 1 }, new int[]{ 1, 2, 3, 3, 3, 3, 3 } },
86          { 4, new int[]{ 1, 2, 3, 4, 5, 6, 7 }, new int[]{ 1, 3, 6, 9, 12, 15, 18 } },
87          { 4, new int[]{ 2, 2, 2, 2, 2, 2, 2 }, new int[]{ 2, 4, 6, 6, 6, 6, 6 } },
88          { 5, new int[]{ 1, 1, 1, 1, 1, 1, 1 }, new int[]{ 1, 2, 3, 4, 4, 4, 4 } },
89          { 5, new int[]{ 1, 2, 3, 4, 5, 6, 7 }, new int[]{ 1, 3, 6, 10, 14, 18, 22 } },
90          { 5, new int[]{ 2, 2, 2, 2, 2, 2, 2 }, new int[]{ 2, 4, 6, 8, 8, 8, 8 } },
91          { 6, new int[]{ 1, 1, 1, 1, 1, 1, 1 }, new int[]{ 1, 2, 3, 4, 5, 5, 5 } },
92          { 6, new int[]{ 1, 2, 3, 4, 5, 6, 7 }, new int[]{ 1, 3, 6, 10, 15, 20, 25 } },
93          { 6, new int[]{ 2, 2, 2, 2, 2, 2, 2 }, new int[]{ 2, 4, 6, 8, 10, 10, 10 } },
94          { 3, new int[]{ 1, 2, 3 }, new int[]{ 1, 3, 5 } } };
95    }
96  
97    @Test(dataProvider = "simulatedTrackerIterations")
98    public void shouldReturnCorrectModifiedTimeWhenMarkedAsModified(int numTimesToTrack,
99        int[] secondsToAdvancePerIteration, int[] expLastModifiedTimes) {
100     // given
101     Time.startSimulating();
102     NthLastModifiedTimeTracker tracker = new NthLastModifiedTimeTracker(numTimesToTrack);
103 
104     int[] modifiedTimes = new int[expLastModifiedTimes.length];
105 
106     // when
107     int i = 0;
108     for (int secondsToAdvance : secondsToAdvancePerIteration) {
109       advanceSimulatedTimeBy(secondsToAdvance);
110       tracker.markAsModified();
111       modifiedTimes[i] = tracker.secondsSinceOldestModification();
112       i++;
113     }
114 
115     // then
116     assertThat(modifiedTimes).isEqualTo(expLastModifiedTimes);
117 
118     // cleanup
119     Time.stopSimulating();
120   }
121 
122   private void advanceSimulatedTimeBy(int seconds) {
123     Time.advanceTime(seconds * MILLIS_IN_SEC);
124   }
125 }